﻿#pragma once
#include <cmath>
#include <iostream>
#include <vector>
#include <iomanip>

namespace sy
{
namespace math
{
	const float Deg2Rad = 0.01745329;	// 角度转弧度乘上这个数
	const float Rad2Deg = 57.2957805;	// 弧度转角度乘上这个数


////////////////////////////////////////////////////////   4 维向量    //////////////////////////////////////////////////////////////////
struct Vector4
{
	/************************************* 数据结构 *****************************************/
	union
	{
		struct
		{
			float x, y, z, w;	// 向量 x, y, z, w
		};
		struct
		{
			float r, g, b, a;	// 颜色 r, g, b, a
		};
		float data[4];

	};

	/************************************* 构造函数(assignment) **************************************/
	Vector4(float _x, float _y, float _z, float _w = 1.0f) :x(_x), y(_y), z(_z), w(_w) {}
	Vector4() :x(0), y(0), z(0), w(0) {}
	Vector4(const Vector4& vec4)
	{
		if (this != &vec4)
		{
			this->x = vec4.x;
			this->y = vec4.y;
			this->z = vec4.z;
			this->w = vec4.w;
		}
	}
	Vector4(const float vec[4]) :x(vec[0]), y(vec[1]), z(vec[2]), w(vec[3]){}

	Vector4& operator=(const Vector4& vec4)
	{
		if (this != &vec4)
		{
			this->x = vec4.x;
			this->y = vec4.y;
			this->z = vec4.z;
			this->w = vec4.w;
		}
		return *this;
	}

	Vector4& operator=(float vec4[4])
	{
		this->x = vec4[0];
		this->y = vec4[1];
		this->z = vec4[2];
		this->w = vec4[3];
		return *this;
	}

public:
	/************************************* 成员函数 **************************************/
	Vector4& Normalize()		/*单位化向量 或 归一化点*/
	{
		Normalize(*this);
		return *this;
	}
	float SqrtMagnitude()	/*求模长平方*/
	{
		return SqrtMagnitude(*this);
	}
	float Magnitude()		/*求模长*/
	{
		return Magnitude(*this);
	}

public:
	/************************************ 重载一些操作符 *************************************/
	// 可以将两个点相加再归一化后求取两个点地中点；俩个向量相加可以得到一个新的向量
	friend Vector4 operator+(const Vector4& vec4A, const Vector4& vec4B);

	// 俩个点相减可以得到一个向量，
	friend Vector4 operator-(const Vector4& vec4A, const Vector4& vec4B);

	friend std::ostream& operator<<(std::ostream& os, Vector4 vec4);

public:
	/************************************** 静态函数（完成基本向量运算）***************************/
	// 返回向量 vec4 的模长的平方
	inline static float SqrtMagnitude(const Vector4& vec4)
	{
		return std::pow(vec4.x, 2) + std::pow(vec4.y, 2) + std::pow(vec4.z, 2);
	}
	// 返回向量 vec4 的模长
	inline static float Magnitude(const Vector4& vec4)
	{
		return std::sqrt(SqrtMagnitude(vec4));
	}
	// 将向量 vec4 单位化,或者将点 vec4 归一化
	inline static void Normalize(Vector4& vec4)
	{
		// vec4是点
		if (vec4.w != 0)
		{
			vec4.x /= vec4.w;
			vec4.y /= vec4.w;
			vec4.z /= vec4.w;
			vec4.w = 1.0f;
		}
		else // vec4 是一个向量
		{
			float magnitude = Magnitude(vec4);
			if (magnitude == 0)
			{
				return;
			}
			vec4.x /= magnitude;
			vec4.y /= magnitude;
			vec4.z /= magnitude;
		}
	}
	// 向量叉积
	static Vector4 Cross(const Vector4& vec4A, const Vector4 vec4B)
	{
		Vector4 ret(0, 0, 0, 0);
		ret.x = (vec4A.y * vec4B.z - vec4A.z * vec4B.y);
		ret.y = (vec4A.z * vec4B.x - vec4A.x * vec4B.z);
		ret.z = (vec4A.x * vec4B.y - vec4A.y * vec4B.x);
		return ret;
	}
	// 向量点积
	static float Dot(const Vector4& vec4A, const Vector4 vec4B)
	{
		return vec4A.x * vec4B.x + vec4A.y * vec4B.y + vec4A.z * vec4B.z;
	}
public:
	/************************************** 静态函数（方便使用）***************************/
	// 返回一个零向量				
	static Vector4 ZeroVector()
	{
		return Vector4(0, 0, 0, 0);
	}
	// 返回一个零点
	static Vector4 ZeroPoint()
	{
		return Vector4(0, 0, 0, 1.0f);
	}
	// 用 (x,y,z,1)表示一个点,用 （x,y,z,0） 表示一个向量
	// 三维坐标系使用左手系 (up)   (forward)              
	//						y    z			
	//                      ^   /                 
	//                      |  /
	//                      | /                 
	//						|/
	//               -------|------------->x(right)                               
	//                     /|
	//                    / |                  
	// 返回一个向前的向量                     
	static Vector4 Forward()
	{
		return Vector4(0, 0, 1.0f, 0);
	}
	// 返回一个向后的向量                     
	static Vector4 Back()
	{
		return Vector4(0, 0, -1.0f, 0);
	}
	// 返回一个向上的向量                     
	static Vector4 Up()
	{
		return Vector4(0, 1.0f, 0, 0);
	}
	// 返回一个向下的向量                     
	static Vector4 Down()
	{
		return Vector4(0, -1.0f, 0, 0);
	}
	// 返回一个向右的向量                     
	static Vector4 Right()
	{
		return Vector4(1.0f, 0, 0, 0);
	}
	// 返回一个向左的向量                     
	static Vector4 Left()
	{
		return Vector4(-1.0f, 0, 0, 0);
	}
};

/******************************** Vector4 的一些重载双目运算符 ***************************************/
Vector4 operator+(const Vector4& vec4A, const Vector4& vec4B)
{
	Vector4 ret(0, 0, 0, 0);
	ret.x = vec4A.x + vec4B.x;
	ret.y = vec4A.y + vec4B.y;
	ret.z = vec4A.z + vec4B.z;
	ret.w = vec4A.w + vec4B.w;
	ret.Normalize();	// 归一化、单位化
	return ret;
}

Vector4 operator-(const Vector4& vec4A, const Vector4& vec4B)
{
	Vector4 ret(0, 0, 0, 0);
	ret.x = vec4A.x - vec4B.x;
	ret.y = vec4A.y - vec4B.y;
	ret.z = vec4A.z - vec4B.z;
	ret.w = vec4A.w - vec4B.w;
	ret.Normalize();	// 归一化、单位化
	return ret;
}

std::ostream& operator<<(std::ostream& os, Vector4 vec4)
{
	os << "(x, y, z, w) = (" << vec4.x << ", " << vec4.y << ", " << vec4.z << ", " << vec4.w << ")" << std::endl;
	return os;
}



////////////////////////////////////////////////////////      矩阵    ////////////////////////////////////////////////////////////////
// 怎么写一个矩阵 https://medium.com/@furkanicus/how-to-create-a-matrix-class-using-c-3641f37809c7
struct Mat4
{
private:
	/********************************************* 属性 ***************************************************/
	// 计算中只需要 4*4 矩阵做变换矩阵，所以直接固定矩阵的大小为4了
	std::vector<std::vector<float> > data;

public:
	/********************************************* 构造函数 *********************************************/
	Mat4() { data.resize(4, std::vector<float>(4, 0));}
	Mat4(const float data[4][4])
	{
		this->data.resize(4);
		for (int i = 0; i < 4; ++i)
		{
			this->data[i] = std::vector<float>(std::begin(data[i]), std::end(data[i]));
		}
	}
	// 深拷贝
	Mat4(const Mat4& mat)
	{
		if (this != &mat)
		{
			this->data = mat.data;	// 使用 vector 的 operator=
		}
	}
	Mat4& operator=(const Mat4& mat)
	{
		if (this != &mat)
		{
			this->data = mat.data;	// 使用 vector 的 operator=
		}
		return *this;
	}
	Mat4& operator=(const float data[4][4])
	{
		this->data.resize(4);
		for (int i = 0; i < 4; ++i)
		{
			this->data[i] = std::vector<float>(std::begin(data[i]), std::end(data[i]));
		}
		return *this;
	}

public:
	/********************************************* 公共方法 **********************************************/
	// 设置行
	void SetRow(int row,const Vector4 &vec4)
	{
		_SetRow_(row, vec4);
	}
	void SetRow(int row, const float arr[4])
	{
		Vector4 vec4(arr);
		_SetRow_(row, vec4);
	}
	// 获取行
	Vector4 GetRow(int row)
	{
		return Vector4(data[row][0], data[row][1], data[row][2], data[row][3]);
	}

	// 设置列
	void SetCol(int col, const float arr[4])
	{
		Vector4 vec4(arr);
		_SetCol_(col, vec4);
	}
	void SetCol(int col, const Vector4& vec4)
	{
		_SetCol_(col, vec4);
	}
	// 获取列
	Vector4 GetCol(int col)
	{
		return Vector4(data[0][col], data[1][col], data[2][col], data[3][col]);
	}
	// 添加一个平移矩阵，也就是左乘平移
	Mat4& AddTranslate(float x, float y, float z)
	{
		Vector4 tmp(x, y, z, 1.0f);
		return AddTranslate(tmp);
	}
	Mat4& AddTranslate(const Vector4 &translate)
	{
		for (int i = 0; i < 3; ++i)
		{
			(*this)[i][3] += translate.data[i];
		}
		return *this;
	}
	Mat4& AddTranslate(Mat4& mat)
	{
		Vector4 tmp = mat.GetCol(3);
		return AddTranslate(tmp);
	}

	// 添加一个旋转矩阵
	Mat4& AddRotate(float x, float y, float z)
	{
		Mat4 _rotate = Mat4::Rotate(x, y, z);
		return AddRotate(_rotate);
	}
	Mat4& AddRotate(const Vector4& rotate)
	{
		Mat4 _rotate = Mat4::Rotate(rotate);
		return AddRotate(_rotate);
	}
	Mat4& AddRotate(const Mat4 &rotate)
	{
		Mat4 tmp = Mat4::Identity();
		for (int i = 0; i < 3; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				float sum = 0;
				for (int k = 0; k < 3; ++k)
				{
					sum += rotate[i][k] * (*this)[k][j];
				}
				tmp[i][j] = sum;
			}
		}
		(*this) = tmp;
		return (*this);
	}
	// 添加一个缩放矩阵
	Mat4& AddScale(float x, float y, float z)
	{
		Vector4 tmp(x, y, z, 1.0f);
		return AddScale(tmp);
	}
	Mat4& AddScale(const Vector4& scale)
	{
		for (int i = 0; i < 3; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				(*this)[i][j] *= scale.data[i];
			}
		}
		return (*this);
	}
	Mat4& AddScale(const Mat4& scale)
	{
		Vector4 tmp;
		for (int i = 0; i < 3; ++i)
		{
			tmp.data[i] = scale[i][i];
		}
		return AddScale(tmp);
	}

	// 添加一个透视矩阵，需要注意的是，这是最后一步了，在添加过透视矩阵后就不应该再添加其它矩阵了
	// 且在整个渲染管线过程中，不应该多次添加透视矩阵，添加一次就够了
	Mat4& AddPerspective(const Mat4 &mat)
	{
		Vector4 tmp = this->GetRow(2);
		for (int i = 0; i < 3; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				(*this)[i][j] *= mat[i][i];
			}
		}
		(*this)[2][3] += mat[2][3];

		for (int i = 0; i < 4; ++i)
		{
			(*this)[3][i] = -tmp.data[i];
		}
		return *this;
	}

	Mat4& AddPerspective(float fov = 45, float aspect = 0.75, float near = 0.3f, float far = 100.0f)
	{
		Mat4 tmp = Mat4::Perspective(fov, aspect, near, far);
		AddPerspective(tmp);
	}


private:
	/********************************************* 私有方法 **********************************************/
	void _SetRow_(int row,const Vector4 &vec4)
	{
		for (int i = 0; i < 4; ++i)
		{
			this->data[row][i] = vec4.data[i];
		}
	}
	void _SetCol_(int col,const Vector4 &vec4)
	{
		for (int i = 0; i < 4; ++i)
		{
			this->data[i][col] = vec4.data[i];
		}
	}

public:
	/********************************************* 运算符重载 **********************************************/
	// 返回 mat[row][col],    行           列
	float operator()(int indexRow, int indexCol) const
	{
		return data[indexRow][indexCol];
	}
	float& operator()(int indexRow, int indexCol)
	{
		return data[indexRow][indexCol];
	}
	const std::vector<float>& operator[](int col) const
	{
		return data[col];
	}
	std::vector<float>& operator[](int col)
	{
		return data[col];
	}
	friend std::ostream& operator<<(std::ostream& os,const Mat4 &mat);

	friend Mat4 operator*(const Mat4& matl, const Mat4& matr)
	{
		Mat4 ret;
		for (int i = 0; i < 4; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				for (int k = 0; k < 4; ++k)
				{
					ret[i][j] += matl[i][k] * matr[k][j];
				}
			}
		}
		return ret;
	}

	friend Vector4 operator*(const Mat4& mat, const Vector4 &vec)
	{
		Vector4 ret;
		for (int i = 0; i < 4; ++i)
		{
			for (int k = 0; k < 4; ++k)
			{
				ret.data[i] += mat[i][k] * vec.data[k];
			}
		}
		return ret;
	}

public:
	/********************************************* 一些静态函数 **********************************************/
	// 返回一个单位矩阵
	static Mat4 Identity()
	{
		Mat4 ret;
		for (int i = 0; i < 4; ++i)
		{
			ret[i][i] = 1.0f;
		}
		return ret;
	}

	// 返回一个平移变换矩阵，vec4是 平移向量(x,y,z) 忽略w值
	static Mat4 Translate(Vector4 vec4)
	{
		vec4.w = 1.0f;
		Mat4 ret = Mat4::Identity();
		ret.SetCol(3, vec4);
		return ret;
	}
	static Mat4 Translate(float x, float y, float z)
	{
		Vector4 vec(x, y, z, 1);
		return Translate(vec);
	}

	// 返回一个旋转变换矩阵，Eular(x,y,z)是旋转的欧拉角（角度值，不是弧度值），这 demo 没有涉及到万向节死锁问题，万向节死锁问题可以通过四元数解决，关于四元数请看 https://krasjet.github.io/quaternion/quaternion.pdf
	// eular 的 w 坐标被忽略 
	static Mat4 Rotate(Vector4 eular)
	{
		using std::cos;
		using std::sin;

		Mat4 ret;
		for (int i = 0; i < 3; ++i)
			eular.data[i] *= Deg2Rad;	// 角度转化为弧度
		float tmp[4][4] =
		{
			{ cos(eular.y)*cos(eular.z), sin(eular.x)*sin(eular.y)*cos(eular.z) - cos(eular.x)*sin(eular.z), sin(eular.x)*sin(eular.z) + cos(eular.x)*sin(eular.y)*cos(eular.z), 0},
			{ cos(eular.y)*sin(eular.z), sin(eular.x)*sin(eular.y)*sin(eular.z) + cos(eular.x)*cos(eular.z), cos(eular.x)*sin(eular.y)*sin(eular.z) - sin(eular.x)*cos(eular.z), 0},
			{			  -sin(eular.y),										  sin(eular.x)*cos(eular.y),										  cos(eular.x)*cos(eular.y), 0},
			{						  0,                                                                  0,                                                                  0, 1},
		};
		ret = tmp;
		return ret;
	}
	static Mat4 Rotate(float x, float y, float z)
	{
		Vector4 tmp(x, y, z);
		return Rotate(tmp);
	}

	// 返回一个缩放矩阵，scale(x,y,z) 为缩放比例，忽略 w 坐标
	static Mat4 Scale(float x, float y, float z)
	{
		Mat4 ret = Identity();
		Vector4 tmp(x, y, z, 1.0f);
		for (int i = 0; i < 3; ++i)
		{
			ret[i][i] = tmp.data[i];
		}
		return ret;
	}
	static Mat4 Scale(Vector4 scale)
	{
		Mat4 ret = Identity();
		for (int i = 0; i < 3; ++i)
		{
			ret[i][i] = scale.data[i];
		}
		return ret;
	}

	// 默认是 45度 fovY, 480 / 640 的 aspect, near默认 0.3， far默认100;
	static Mat4 Perspective(float fov = 45, float aspect = 0.75, float near = 0.3f, float far = 100.0f)
	{
		using std::tan;
		float tanHelfFov = tan((fov * Deg2Rad / 2.0f));
		Mat4 ret;
		ret[0][0] = 1.0 / (tanHelfFov * aspect);
		ret[1][1] = 1.0 / tanHelfFov;
		ret[2][2] = -(far + near) / (far - near);
		ret[2][3] = -(2.0f * far * near) / (far - near);
		ret[3][2] = -1;
		return ret;
	}
};
// mat4
/********************************************* 运算符重载 **********************************************/
std::ostream& operator<<(std::ostream& os,const Mat4 &mat)
{
	for (int i = 0; i < 4; ++i)
	{
		os << "{ ";
		for (int j = 0; j < 4; ++j)
		{
			os << std::setw(12) << mat.data[i][j] << ", ";
		}
		os << "}" << std::endl;
	}
	return os;
}
}
}